﻿using System;
using System.Collections.Generic;
using System.Text;
using Tao.OpenGl;
using PacMan3D.Utils;
using System.IO;
using Tao.DevIl;
using PacMan3D.Exceptions;

namespace PacMan3D.Objects
{
    /// <summary>
    /// This class represents a Platform.
    /// </summary>
    public class Platform : IRenderable
    {
        
        public int Width { get; private set; }
        public int Height { get; private set; }

        public float PieceSize {get; private set;}
        private int DisplayListId = 0;
        private int TextureId = 0;
        
        public Platform(int width, int height, float pieceSize)
        {
            this.Width = width;
            this.Height = height;
            this.PieceSize = pieceSize;

            this.TextureId = ResourceLoader.LoadResource(Constants.RES_TEXT_PLATFORM);

            // display list - lista que contém a peça da plataforma
            this.DisplayListId = Gl.glGenLists(1);
            Gl.glNewList(this.DisplayListId, Gl.GL_COMPILE);
            Gl.glBegin(Gl.GL_QUADS);
            Gl.glNormal3f(0.0f, 1.0f, 0.0f);
            Gl.glTexCoord2f(0.0f, 1.0f); Gl.glVertex3f(-this.PieceSize / 2, 0, -this.PieceSize / 2);
            Gl.glTexCoord2f(0.0f, 0.0f); Gl.glVertex3f(-this.PieceSize / 2, 0, this.PieceSize / 2);
            Gl.glTexCoord2f(1.0f, 0.0f); Gl.glVertex3f(this.PieceSize / 2, 0, this.PieceSize / 2);
            Gl.glTexCoord2f(1.0f, 1.0f); Gl.glVertex3f(this.PieceSize / 2, 0, -this.PieceSize / 2);
            Gl.glEnd();
            Gl.glEndList();
        }

        /// <summary>
        /// Método que desenha o piso na cena.
        /// </summary>
        public override void DrawObject()
        {
            Gl.glEnable(Gl.GL_TEXTURE_2D);
            Gl.glBindTexture(Gl.GL_TEXTURE_2D, this.TextureId);
            Gl.glTexEnvf(Gl.GL_TEXTURE_ENV, Gl.GL_TEXTURE_ENV_MODE, Gl.GL_DECAL);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MAG_FILTER, Gl.GL_LINEAR);
            Gl.glTexParameteri(Gl.GL_TEXTURE_2D, Gl.GL_TEXTURE_MIN_FILTER, Gl.GL_LINEAR);
            for (int i = 0; i < this.Width; i++)
            {
                for (int j = 0; j < this.Height; j++)
                {
                    Gl.glPushMatrix();
                        Gl.glTranslatef(j * this.PieceSize, 0, i * this.PieceSize);
                        Gl.glCallList(this.DisplayListId);
                    Gl.glPopMatrix();
                }
            }
            Gl.glDisable(Gl.GL_TEXTURE_2D);
        }

        /// <summary>
        /// Método que devolve o ponto central da plataforma.
        /// </summary>
        /// <returns></returns>
        public Point3D GetCentralPoint()
        {
            return new Point3D((this.Height * this.PieceSize) / 2, 0, (this.Width * this.PieceSize) / 2);
        }

        /// <summary>
        /// Método que devolve o tamanho da plataforma.
        /// </summary>
        /// <returns></returns>
        public Size GetSize()
        {
            return new Size(this.Height * this.PieceSize, 0, this.Width * this.PieceSize);
        }

        /// <summary>
        /// Método que obtém o plano da plataforma.
        /// </summary>
        /// <returns></returns>
        public float[] GetFPlane()
        {
            return new float[] { 0.0f, 1.0f, 0.0f, 0.0f };
        }

        /*
        /// <summary>
        /// Step 1: Limiting the rendering of the shadows.
        /// http://www.devmaster.net/articles/projectiveshadows/
        /// </summary>
        public void InitializeForShadow()
        {
            // turning off writing to the color buffer and depth buffer so we only write to stencil buffer.
            Gl.glColorMask(Gl.GL_FALSE, Gl.GL_FALSE, Gl.GL_FALSE, Gl.GL_FALSE);
            Gl.glDepthMask(Gl.GL_FALSE);
            // enable stencil buffer
            Gl.glEnable(Gl.GL_STENCIL_TEST);
            // write a one to the stencil buffer everywhere we are about to draw
            Gl.glStencilFunc(Gl.GL_ALWAYS, 1, 0xFFFFFFFF);
            // this is to always pass a one to the stencil buffer where we draw
            Gl.glStencilOp(Gl.GL_REPLACE, Gl.GL_REPLACE, Gl.GL_REPLACE);

            this.DrawObject();

            // turn the color and depth buffers back on
            Gl.glColorMask(Gl.GL_TRUE, Gl.GL_TRUE, Gl.GL_TRUE, Gl.GL_TRUE);
            Gl.glDepthMask(Gl.GL_TRUE);

            // until stencil test is diabled, only write to areas where the
            // stencil buffer has a one. This is to draw the shadow only on
            // the floor.
            Gl.glStencilFunc(Gl.GL_EQUAL, 1, 0xFFFFFFFF);

            // don't modify the contents of the stencil buffer
            Gl.glStencilOp(Gl.GL_KEEP, Gl.GL_KEEP, Gl.GL_KEEP);
        }
        */
    }
}
